﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using AGPR.Locadora.Data;
using AGPR.Locadora.Utils;

namespace AGPR.Locadora.Model
{
    /// <summary>
    /// Classe de Funcionário que extende a Classe Abstrata Pessoa
    /// </summary>
    public class Funcionario : Pessoa
    {
        private static ActiveRecord db = ActiveRecord.Instancia;

        #region Atributos

        const string TABELA = "funcionario";
        const string VIEW = "vw_funcionario";
        
        /// <summary>
        /// Código do Funcionario
        /// </summary>
        [Display(Name = "Código do Funcionario")]
        public int id { get; set; }

        /// <summary>
        /// Login de Autenticação
        /// </summary>
        [Required]
        [Display(Name = "Login")]
        public string login { get; set; }

        /// <summary>
        /// Senha de Autenticação
        /// </summary>
        [Display(Name = "Senha")]
        public string senha { get; set; }

        /// <summary>
        /// Nível de Acesso
        /// </summary>
        [Display(Name = "Nível")]
        public string nivel { get; set; }

        /// <summary>
        /// Data de Cadastro
        /// </summary>
        [Display(Name = "Data de Cadastro")]
        public DateTime data_cadastro { get; set; }

        /// <summary>
        /// Código do Funcionário Superior
        /// </summary>
        [Display(Name = "Código do Funcionário")]
        public int? id_superior { get; set; }

        /// <summary>
        /// Funcionário Superior
        /// </summary>
        public Funcionario superior { get; set; }

        #endregion

        #region Create

        /// <summary>
        /// Retorna uma nova instância de Funcionario populada
        /// </summary>
        /// <param name="dados">Dados</param>
        /// <returns>Funcionario</returns>
        public static Funcionario Create(dynamic dados = null)
        {
            Funcionario func = new Funcionario();

            if (dados != null)
            {
                func.id = Convert.ToInt32(dados["id_funcionario"]);
                func.id_superior = Convert.ToInt32(dados["id_superior"]);
                func.id_endereco = Convert.ToInt32(dados["id_endereco"]);
                func.endereco = Endereco.Create(dados);
                func.nome = dados["nome"];
                func.cpf = dados["cpf"];
                func.email = dados["email"];
                func.sexo = dados["sexo"];
                func.login = dados["login"];
                func.senha = dados["senha"];
                func.data_nascimento = dados["data_nascimento"];
                func.data_cadastro = Convert.ToDateTime(dados["data_cadastro"]);
                func.ativoCheckbox = dados["ativo"];
            }

            return func;
        }

        #endregion

        #region getNextID

        /// <summary>
        /// Retorna o Proximo ID
        /// </summary>
        /// <returns>ID</returns>
        private int getNextID()
        {
            return db.max("id_funcionario", TABELA) + 1;
        }

        #endregion

        #region Cadastrar

        /// <summary>
        /// Cadastra o Funcionário no Banco de Dados
        /// </summary>
        /// <returns>Funcionario</returns>
        public Funcionario Cadastrar()
        {
            this.id = this.id > 0 ? this.id : this.getNextID();

            this.endereco.Cadastrar();
            this.id_endereco = this.endereco.id;

            db.insert(TABELA, new Dictionary<string, dynamic>
            {
                {"id_funcionario", this.id},
                {"id_endereco", this.id_endereco},
                {"nome", this.nome},
                {"cpf", Format.limparNumero(this.cpf)},
                {"email", this.email},
                {"sexo", this.sexo},
                {"login", this.login},
                {"senha", Criptografia.md5(this.senha)},
                {"nivel", 1},
                {"data_nascimento", this.data_nascimento},
                {"ativo", this.ativo}
            });

            return this;
        }

        #endregion

        #region Alterar

        /// <summary>
        /// Altera o Funcionario no Banco de Dados
        /// </summary>
        /// <returns>Funcionario</returns>
        public Funcionario Alterar()
        {
            if (this.id > 0)
            {
                this.endereco.Alterar();

                db.where("id_funcionario", this.id);
                db.update(TABELA, new Dictionary<string, dynamic>
                {
                    {"nome", this.nome},
                    {"cpf", Format.limparNumero(this.cpf)},
                    {"sexo", this.sexo},
                    {"email", this.email},
                    {"login", this.login},
                    {"nivel", 1},
                    {"data_nascimento", this.data_nascimento},
                    {"ativo", this.ativo}
                });
            }

            return this;
        }

        #endregion

        #region Alterar Senha

        /// <summary>
        /// Alterar Senha
        /// </summary>
        /// <param name="senhaAtual">Senha Atual para Verificação</param>
        /// <param name="novaSenha">Nova Senha</param>
        /// <returns>Funcionario</returns>
        public Funcionario AlterarSenha(string senhaAtual, string novaSenha)
        {
            senhaAtual = Criptografia.md5(senhaAtual);
            novaSenha = Criptografia.md5(novaSenha);

            if (senhaAtual == this.senha)
            {
                this.senha = novaSenha;

                db.where("id_funcionario", this.id);
                db.update(TABELA, "senha", this.senha);
            }
            
            return this;
        }

        #endregion

        #region Excluir

        /// <summary>
        /// Exclui um Funcionário da base de dados
        /// </summary>
        /// <returns>Funcionario</returns>
        public Funcionario Excluir()
        {
            if (this.id > 0)
            {
                // Só excluo se não tiver nenhuma locação
                if (this.TotalLocacoes() == 0)
                {
                    db.startTransaction();
                    db.where("id_funcionario", this.id).delete(TABELA);
                    db.where("id_endereco", this.id_endereco).delete(Endereco.TABELA);
                    db.commit();
                }
            }

            return this;
        }

        #endregion

        #region Login

        /// <summary>
        /// Retorna um usuário com o login e senha especificado
        /// </summary>
        /// <param name="login">Login</param>
        /// <param name="senha">Senha</param>
        /// <returns>Funcionario</returns>
        public static Funcionario Login(string login, string senha)
        {
            db.where("login", login).where("senha", Criptografia.md5(senha));

            return Funcionario.Get();
        }

        #endregion

        #region Get

        /// <summary>
        /// Retorna um Funcionário (Sem Condições WHERE)
        /// As condições são usadas em outros métodos
        /// </summary>
        /// <returns>Funcionario</returns>
        public static Funcionario Get()
        {
            var func = db.selectOne(VIEW);

            return func != null ? Funcionario.Create(func) : null;
        }

        /// <summary>
        /// Retorna um Funcionario pelo ID
        /// </summary>
        /// <param name="id">ID do Funcionario</param>
        /// <returns>Funcionario</returns>
        public static Funcionario Get(dynamic id)
        {
            db.where("id_funcionario", id);

            return Funcionario.Get();
        }

        /// <summary>
        /// Retorna uma lista de todos Funcionários
        /// </summary>
        /// <returns>Lista de Funcionários</returns>
        public static List<Funcionario> GetAll(int offset = 0)
        {
            List<Funcionario> funcs = new List<Funcionario>();

            db.order_by("nome").limit(offset, Config.PageLimit);

            foreach (var func in db.select(VIEW))
            {
                funcs.Add(Funcionario.Create(func));
            }

            return funcs;
        }

        #endregion

        #region Total

        /// <summary>
        /// Retorna o Total de Funcionários Cadastrados
        /// </summary>
        /// <returns>Total</returns>
        public static int Total()
        {
            return db.count(VIEW);
        }

        #endregion

        #region TotalLocacoes

        /// <summary>
        /// Retorna o Total de Locações deste Funcionário
        /// </summary>
        /// <returns>Total</returns>
        public int TotalLocacoes()
        {
            db.where("id_funcionario", this.id);
            return db.count(Locacao.TABELA);
        }

        #endregion
    }
}
